/*
 * Copyright (C) 2010 M. Hautle.
 * 
 * This file is part of SimpleEL.
 * 
 * SimpleEL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * SimpleEL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with SimpleEL. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.simpleel;

import java.util.*;
import java.util.regex.*;
import ch.simpleel.accessors.*;
import ch.simpleel.internal.*;
import ch.simpleel.util.*;

/**
 * Object holding an simple EL expressions embedded in a string.<br>
 * The EL expression parts have to be encapsulated in <code>${el}</code> blocks.<br>
 * See {@link ELHelper} for more information about simple EL expressions.
 * 
 * @author M. Hautle
 */
public class ELExpression {
    /** Pattern used to extract the el parts out of an expression. */
    private static final Pattern EL_EXTRACTER = Pattern.compile("\\$\\{(.+?)\\}");

    /** The entries representing this expression. */
    private final IValueAccessor[] entries;

    /** The value for undefined expressions. */
    private final String undefined;

    /**
     * Default constructor.<br>
     * 
     * @param el An string containing el expression escaped in <code>${el}</code> blocks
     * @param undefined The string to use for undefined values
     */
    @SuppressWarnings("unchecked")
    public ELExpression(String el, String undefined) {
        this.undefined = undefined;
        final List<IValueAccessor> res = new ArrayList<IValueAccessor>();
        int i = 0;
        // split up the string into el and non el expressions
        for (Matcher m = EL_EXTRACTER.matcher(el); m.find(); i = m.end()) {
            int start = m.start();
            if (i < start)
                res.add(new StringEntry(el.substring(i, start)));
            res.add(new ELAccessor(m.group(1), Object.class));
        }
        if (i < el.length())
            res.add(new StringEntry(el.substring(i)));
        entries = res.toArray(new IValueAccessor[res.size()]);
    }

    /**
     * Evaluates the given expression.
     * 
     * @param bean The bean on which the expression will be evaluated
     * @return The evaluation result
     */
    @SuppressWarnings("unchecked")
    public String evaluate(Object bean) {
        final StringBuilder str = new StringBuilder();
        for (IValueAccessor e : entries)
            str.append(e.getValue(bean, undefined));
        return str.toString();
    }

    /**
     * Entry holding a simple {@link String}.
     * 
     * @author M. Hautle
     */
    private static class StringEntry implements IValueAccessor<String> {
        /** The text representing this entry. */
        private final String txt;

        /**
         * Default constructor.
         * 
         * @param txt The text representing this entry
         */
        StringEntry(String txt) {
            this.txt = txt;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getPath() {
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getValue(Object bean, String fallback) {
            return txt;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean setValue(Object bean, String value) {
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Class<String> getType() {
            return String.class;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isReadable() {
            return true;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isWritable() {
            return false;
        }
    }
}
